{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0e1bef9b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import os\n",
    "\n",
    "IMAGE_ROOT = '/data/user/zhangdonghao/openCV_test/Template'\n",
    "\n",
    "# 封装成小函数\n",
    "def cv_show(name, img):\n",
    "    cv2.imshow(name, img)\n",
    "    cv2.waitKey()\n",
    "    cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b0be1e8",
   "metadata": {},
   "source": [
    "##### 图像梯度-Sobel算子"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a35e9b8f",
   "metadata": {},
   "source": [
    "$$ \\mathbf{G}_{x} = \\begin{bmatrix} -1 & 0 & +1 \\\\ -2 & 0 & +2 \\\\ -1 & 0 & +1 \\end{bmatrix} * \\mathbf{A} \\quad \\text{and} \\quad \\mathbf{G}_{y} = \\begin{bmatrix} -1 & -2 & -1 \\\\ 0 & 0 & 0 \\\\ +1 & +2 & +1 \\end{bmatrix} * \\mathbf{A} $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7ba64259",
   "metadata": {},
   "outputs": [],
   "source": [
    "img = cv2.imread(os.path.join(IMAGE_ROOT, 'erode_dilate_test.png'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91219c37",
   "metadata": {},
   "source": [
    "dst = cv2.Sobel(src, ddepth, dx, dy, ksize)\n",
    "* ddepth：图像的深度\n",
    "* dx和dy表示水平和竖直方向\n",
    "* ksize是Sobel算子的大小"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "6bbf32a3",
   "metadata": {},
   "outputs": [
    {
     "ename": "",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m在当前单元格或上一个单元格中执行代码时 Kernel 崩溃。\n",
      "\u001b[1;31m请查看单元格中的代码，以确定故障的可能原因。\n",
      "\u001b[1;31m单击<a href='https://aka.ms/vscodeJupyterKernelCrash'>此处</a>了解详细信息。\n",
      "\u001b[1;31m有关更多详细信息，请查看 Jupyter <a href='command:jupyter.viewOutput'>log</a>。"
     ]
    }
   ],
   "source": [
    "sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3) # cv2.CV_64F代表64位浮点数\n",
    "cv_show(\"sobelx\", sobelx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ac344417",
   "metadata": {},
   "outputs": [
    {
     "ename": "",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m在当前单元格或上一个单元格中执行代码时 Kernel 崩溃。\n",
      "\u001b[1;31m请查看单元格中的代码，以确定故障的可能原因。\n",
      "\u001b[1;31m单击<a href='https://aka.ms/vscodeJupyterKernelCrash'>此处</a>了解详细信息。\n",
      "\u001b[1;31m有关更多详细信息，请查看 Jupyter <a href='command:jupyter.viewOutput'>log</a>。"
     ]
    }
   ],
   "source": [
    "# （右边-左边 > 0 or < 0）白到黑是正数，黑到白就是负数了，所有的负数会被截断成0，因此需要取绝对值\n",
    "sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3)\n",
    "sobelx = cv2.convertScaleAbs(sobelx)\n",
    "cv_show(\"sobelx\", sobelx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8451f007",
   "metadata": {},
   "outputs": [],
   "source": [
    "# y方向\n",
    "sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize = 3)\n",
    "sobely = cv2.convertScaleAbs(sobely)\n",
    "cv_show(\"sobely\", sobely)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "46742607",
   "metadata": {},
   "outputs": [],
   "source": [
    "# x, y方向加权求和\n",
    "sobelxy = cv2.addWeighted(sobelx, 0.5 ,sobely, 0.5, 0)\n",
    "sobelxy = cv2.convertScaleAbs(sobelxy)\n",
    "cv_show(\"sobelxy\", sobelxy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e7b40e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 直接计算缺点, 有重影，部分区域不太连贯\n",
    "sobelxy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize = 3)\n",
    "sobelxy = cv2.convertScaleAbs(sobelxy)\n",
    "cv_show(\"sobelxy\", sobelxy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a20242db",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 举例\n",
    "img2 = cv2.imread(os.path.join(IMAGE_ROOT, 'warrior-hencedpower.png'), cv2.IMREAD_GRAYSCALE)\n",
    "\n",
    "sobelx = cv2.Sobel(img2, cv2.CV_64F, 1, 0, ksize = 3)\n",
    "sobelx = cv2.convertScaleAbs(sobelx)\n",
    "\n",
    "sobely = cv2.Sobel(img2, cv2.CV_64F, 0, 1, ksize = 3)\n",
    "sobely = cv2.convertScaleAbs(sobely)\n",
    "\n",
    "sobelxy = cv2.addWeighted(sobelx, 0.5 ,sobely, 0.5, 0)\n",
    "sobelxy = cv2.convertScaleAbs(sobelxy)\n",
    "\n",
    "cv_show(\"sobelxy\", sobelxy)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "044d3410",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 整体呢？- 不好！\n",
    "sobelxy = cv2.Sobel(img2, cv2.CV_64F, 1, 1, ksize = 3)\n",
    "sobelxy = cv2.convertScaleAbs(sobelxy)\n",
    "cv_show(\"sobelxy\", sobelxy)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "489b71ae",
   "metadata": {},
   "source": [
    "##### 图像梯度-Scharr算子、Laplacian算子\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e079dbfd",
   "metadata": {},
   "source": [
    "**图像梯度-Scharr算子**\n",
    "\n",
    "$$ \\mathbf{G}_{x}=\\begin{bmatrix} -3 & 0 & 3 \\\\ -10 & 0 & 10 \\\\ -3 & 0 & 3 \\end{bmatrix} * \\mathbf{A} \\quad \\text{and} \\quad \\mathbf{G}_{y}=\\begin{bmatrix} -3 & -10 & -3 \\\\ 0 & 0 & 0 \\\\ 3 & 10 & 3 \\end{bmatrix} * \\mathbf{A} $$\n",
    "\n",
    "**图像梯度-Laplacian算子**\n",
    "\n",
    "$$ \\mathbf{G}=\\begin{bmatrix} 0 & 1 & 0 \\\\ 1 & -4 & 1 \\\\ 0 & 1 & 0 \\end{bmatrix} * \\mathbf{A} $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3aec2448",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 不同算子之间的差异\n",
    "img3 = cv2.imread(os.path.join(IMAGE_ROOT, 'warrior-hencedpower.png'), cv2.IMREAD_GRAYSCALE)\n",
    "\n",
    "# Sobel算子\n",
    "sobelx = cv2.Sobel(img3, cv2.CV_64F, 1, 0, ksize = 3)\n",
    "sobelx = cv2.convertScaleAbs(sobelx)\n",
    "sobely = cv2.Sobel(img3, cv2.CV_64F, 0, 1, ksize = 3)\n",
    "sobely = cv2.convertScaleAbs(sobely)\n",
    "sobelxy = cv2.addWeighted(sobelx, 0.5 ,sobely, 0.5, 0)\n",
    "\n",
    "# Scharr算子\n",
    "scharrx = cv2.Scharr(img3, cv2.CV_64F, 1, 0)\n",
    "scharrx = cv2.convertScaleAbs(scharrx)\n",
    "scharry = cv2.Sobel(img3, cv2.CV_64F, 0, 1)\n",
    "scharry = cv2.convertScaleAbs(scharry)\n",
    "scharrxy = cv2.addWeighted(scharrx, 0.5 ,scharry, 0.5, 0)\n",
    "\n",
    "# Laplacian算子\n",
    "laplacian =cv2.Laplacian(img3, cv2.CV_64F)\n",
    "laplacian =cv2.convertScaleAbs(laplacian)\n",
    "\n",
    "res = np.hstack((sobelxy, scharrxy, laplacian))\n",
    "cv_show(\"res\", res)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3943d174",
   "metadata": {},
   "source": [
    "##### Sobel、Scharr 与 Laplacian 算子对比\n",
    "\n",
    "| 特性 | Sobel 算子 | Scharr 算子 | Laplacian 算子 |\n",
    "| :--- | :--- | :--- | :--- |\n",
    "| **OpenCV 函数** | `cv2.Sobel(src, ddepth, dx, dy[, ksize])` | `cv2.Scharr(src, ddepth, dx, dy)` | `cv2.Laplacian(src, ddepth[, ksize])` |\n",
    "| **卷积核 (数学表示)** | Gx = $\\begin{bmatrix} -1 & 0 & 1 \\\\ -2 & 0 & 2 \\\\ -1 & 0 & 1 \\end{bmatrix} * A$ <br> Gy = $\\begin{bmatrix} -1 & -2 & -1 \\\\ 0 & 0 & 0 \\\\ 1 & 2 & 1 \\end{bmatrix} * A$ | Gx = $\\begin{bmatrix} -3 & 0 & 3 \\\\ -10 & 0 & 10 \\\\ -3 & 0 & 3 \\end{bmatrix} * A$ <br> Gy = $\\begin{bmatrix} -3 & -10 & -3 \\\\ 0 & 0 & 0 \\\\ 3 & 10 & 3 \\end{bmatrix} * A$ | $\\begin{bmatrix} 0 & 1 & 0 \\\\ 1 & -4 & 1 \\\\ 0 & 1 & 0 \\end{bmatrix} * A$ <br> (4邻域) |\n",
    "| **导数阶数** | 一阶导数(@ref) | 一阶导数(@ref) | **二阶导数**(@ref) |\n",
    "| **方向性** | 是 (分x和y方向)(@ref) | 是 (分x和y方向)(@ref) | **否** (各向同性)(@ref) |\n",
    "| **抗噪能力** | 较好 (结合了平滑)(@ref) | 与Sobel相近，对细节更敏感(@ref) | **较弱**，对噪声非常敏感(@ref) |\n",
    "| **主要优点** | 计算简单快速，有一定的抗噪能力(@ref) | 对边缘**更敏感**，细节保留更好，旋转不变性更佳(@ref) | 能检测**所有方向**的边缘，对细节和突变敏感(@ref) |\n",
    "| **主要缺点** | 边缘可能较粗，对细节和斜向边缘响应一般(@ref) | 对噪声可能比Sobel更敏感一些(@ref) | 易产生孤立边缘或双边缘，通常需先进行平滑处理(@ref) |\n",
    "| **输出处理** | 需`cv2.convertScaleAbs()`处理负值(@ref) | 需`cv2.convertScaleAbs()`处理负值(@ref) | 需`cv2.convertScaleAbs()`处理负值[(@ref) |\n",
    "| **典型应用场景** | 通用边缘检测、快速计算梯度方向(@ref) | 需要高精度梯度和细节边缘的场景(@ref) | 图像锐化、斑点检测(Blob)(@ref) |"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
